import _ from 'lodash-es'

const toString = Object.prototype.toString

export function is(val: unknown, type: string) {
  return toString.call(val) === `[object ${type}]`
}

export function isDef<T = unknown>(val?: T): val is T {
  return typeof val !== 'undefined'
}

export function isUnDef<T = unknown>(val?: T): val is T {
  return !isDef(val)
}

export function isObject(val: any): val is Record<any, any> {
  return val !== null && is(val, 'Object')
}

export function isEmpty<T = unknown>(val: T): val is T {
  if (isArray(val) || isString(val)) {
    return val.length === 0
  }

  if (val instanceof Map || val instanceof Set) {
    return val.size === 0
  }

  if (isObject(val)) {
    return Object.keys(val).length === 0
  }

  return false
}

export function isDate(val: unknown): val is Date {
  return is(val, 'Date')
}

export function isNull(val: unknown): val is null {
  return val === null
}

export function isNullAndUnDef(val: unknown): val is null | undefined {
  return isUnDef(val) && isNull(val)
}

export function isNullOrUnDef(val: unknown): val is null | undefined {
  return isUnDef(val) || isNull(val)
}

export function isNumber(val: unknown): val is number {
  return is(val, 'Number')
}

export function isPromise<T = any>(val: unknown): val is Promise<T> {
  return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
}

export function isString(val: unknown): val is string {
  return is(val, 'String')
}

export function isFunction(val: unknown): val is Fn {
  return typeof val === 'function'
}

export function isBoolean(val: unknown): val is boolean {
  return is(val, 'Boolean')
}

export function isRegExp(val: unknown): val is RegExp {
  return is(val, 'RegExp')
}

export function isArray(val: any): val is Array<any> {
  return val && Array.isArray(val)
}

export function isWindow(val: any): val is Window {
  return typeof window !== 'undefined' && is(val, 'Window')
}

export function isElement(val: unknown): val is Element {
  return isObject(val) && !!val.tagName
}

export function isMap(val: unknown): val is Map<any, any> {
  return is(val, 'Map')
}

export const isServer = typeof window === 'undefined'

export const isClient = !isServer

export function isUrl(path: string): boolean {
  const reg =
    // eslint-disable-next-line
    /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/
  return reg.test(path)
}

export function isNumeric(val): val is number {
  return !isNaN(parseFloat(val)) && isFinite(val)
}

export function isNoZero(val) {
  // 避免 .00 这种情况
  if (val && val !== '') {
    const num = parseFloat(val)
    return !isNaN(num) && num !== 0
  }
  return false
}

/** 递归判断一个值是否全为 '' undefined null NaN */
export function isDataEmpty(data, exclude: string[] = []) {
  if (!data && data !== 0) return true
  if (isNumber(data) || isString(data) || isFunction(data)) return false
  if (isArray(data)) return data.every((item) => isDataEmpty(item))
  if (isObject(data))
    return isDataEmpty(
      Object.keys(data)
        .filter((key) => !exclude.includes(key))
        .map((key) => data[key])
    )
}

/** 判断两个字符串是否相等 */
export const isStrEquality = (val: string, str: string) => {
  return typeof val === 'string' && val.toUpperCase() === str.toUpperCase()
}

/** 判断字符串是否包含指定字符串 */
export const isStrIncludes = (val: string, search: string | string[]) => {
  if (typeof val !== 'string') return false

  const valUpper = val.toUpperCase()

  if (Array.isArray(search)) {
    return search.some((s) => typeof s === 'string' && valUpper.includes(s.toUpperCase()))
  }

  return typeof search === 'string' && valUpper.includes(search.toUpperCase())
}
